Skip to content

fix(cronos): add contract authorization check to SendCroToIbcHandler#2099

Merged
thomas-nguy merged 23 commits into
crypto-org-chain:mainfrom
JayT106:jt/fix-ibc-verification
Jun 17, 2026
Merged

fix(cronos): add contract authorization check to SendCroToIbcHandler#2099
thomas-nguy merged 23 commits into
crypto-org-chain:mainfrom
JayT106:jt/fix-ibc-verification

Conversation

@JayT106

@JayT106 JayT106 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Any EVM contract emitting __CronosSendCroToIbc could drain CRO via IBC without being registered or authorized. Sister handlers (SendToIbc, SendToAccount) already gate on contract registration; this handler had no equivalent guard.

Changes

  • CroBridgeContractAddresses ([]string) to support multiple bridge implementations and testnets
  • SendCroToIbcHandler rejects callers not in the authorized list; empty list (safe default) disables the hook entirely
  • RegisterUpgradeHandlers panics at startup if any entry in mainnetCroBridgeContractAddresses is invalid — catches misconfiguration before upgrade height
  • v1.8 upgrade handler migrates the param (mainnetCroBridgeContractAddresses must be filled in before release)

Any EVM contract emitting __CronosSendCroToIbc could drain CRO via IBC
without being registered or authorized. Sister handlers (SendToIbc,
SendToAccount) already gate on contract registration; this handler had
no equivalent guard.

Fix:
- Add CroBridgeContractAddress string param to cronos module params
- SendCroToIbcHandler rejects callers that don't match the authorized
  address; empty param (safe default) disables the hook entirely
- validateIsEvmAddress rejects non-hex strings and the zero address
- v1.8 upgrade handler migrates the param (mainnetCroBridgeContractAddress
  constant must be filled in before release)

Tests:
- Unit: two new rejection cases + existing success cases updated to set
  the authorized address before calling the handler
- Go upgrade test: validates constant format + SetParams round-trip
- Integration upgrade test: asserts param is "" after v1.8 handler runs
- Integration IBC test: authorizes deployed CroBridge via gov proposal
  before bridge call (fixes test breakage from the new guard)
@JayT106
JayT106 requested a review from a team as a code owner June 5, 2026 01:40
@JayT106
JayT106 requested review from ApacCronos and randy-cro June 5, 2026 01:40
@JayT106 JayT106 self-assigned this Jun 5, 2026
@github-actions

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a repeated cro_bridge_contract_addresses Params field with validation, initializes it during v1.8 upgrade, enforces it in SendCroToIbcHandler (error on missing/mismatched emitter), and updates unit/integration tests plus testenv wheel handling.

Changes

CroBridge Contract Authorization

Layer / File(s) Summary
Parameter contract and validation
proto/cronos/cronos.proto, x/cronos/types/params.go
Params gains cro_bridge_contract_addresses (repeated string); types add KeyCroBridgeContractAddresses, extend NewParams/DefaultParams, register the param, and add validateIsEvmAddresses/validateIsEvmAddress allowing empty or requiring a non-zero EVM hex address.
Handler enforcement and unit tests
x/cronos/keeper/evmhandlers/send_cro_to_ibc.go, x/cronos/keeper/evmhandlers_test.go, CHANGELOG.md
Handler fetches CroBridgeContractAddresses from params and returns an authorization error if the emitting contract is not authorized. Tests add unauthorized/mismatch cases and adjust authorized flows; changelog notes the change.
Upgrade initialization and tests
app/upgrades.go, app/upgrades_test.go, x/cronos/simulation/genesis.go
v1.8 upgrade handler writes CroBridgeContractAddresses from mainnetCroBridgeContractAddresses and fails on SetParams error; upgrade test validates addresses and params persistence; simulation genesis updated for new NewParams arg.
Integration test governance and upgrade checks
integration_tests/test_upgrade.py, integration_tests/test_ibc.py, integration_tests/test_gov_update_params.py
Integration IBC test submits a governance /cronos.MsgUpdateParams proposal to set the authorized bridge contract before exercising the hook; upgrade integration test asserts the param is empty after v1.8; gov update test includes the new param field.
Testenv wheel handling adjustments
nix/testenv.nix
poetry2nix overrides prefer wheels and skip wheel-incompatible patch/config phases for wheel distributions; pyproject postPatch edits are gated to non-wheel sources.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

cronos

Suggested reviewers

  • songgaoye
  • thomas-nguy
  • calvinaco

Poem

🐰 I hop where bridges guard the chain,
Empty by default, then whitelisted by name,
Params hold the door, a tidy little list,
Only trusted contracts make the CRO persist,
Hooray — safe hops across the cryptic mist.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main change: adding a contract authorization check to SendCroToIbcHandler, which is the primary security fix across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@JayT106
JayT106 requested a review from thomas-nguy June 5, 2026 01:40
@JayT106

JayT106 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

@claude

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @JayT106's task in 2m 24s —— View job


PR Review: fix(cronos): add contract authorization check to SendCroToIbcHandler

  • Read CLAUDE.md and understand repo guidelines
  • Read changed files
  • Analyze security implications
  • Check for state-breaking changes / consensus impact
  • Post review findings

Summary

This PR closes a real authorization gap: any EVM contract emitting __CronosSendCroToIbc could previously drain CRO over IBC without any registration check, unlike the sibling SendToIbc/SendToAccount handlers which guard on GetDenomByContract. The fix is sound in design. Below are the issues found, ordered by severity.


State / Consensus Impact

This is a state-breaking change requiring a coordinated upgrade. The Params protobuf message gains a new field (cro_bridge_contract_address, field number 6), and the upgrade handler writes it to the store. All validators must upgrade to v1.8 before the upgrade block height — nodes on older binaries will not deserialize the updated params correctly. This is handled through the existing v1.8 upgrade plan, which is the correct mechanism. ✅


Issues Found

Critical: mainnetCroBridgeContractAddress is empty — the fix is not live until set

app/upgrades.go:25

const mainnetCroBridgeContractAddress = ""

The PR comment acknowledges this: "mainnetCroBridgeContractAddress constant must be filled in before release". But as-is, the upgrade handler will write an empty string, which means the SendCroToIbc hook is entirely disabled on mainnet after v1.8. This is called out as intentional ("safe default"), but it must be tracked as a hard pre-release gate. The upgrade test in test_upgrade.py currently asserts the empty string, which means CI will pass and the release could ship without anyone filling it in.

Consider adding a CI-level check (or at minimum a TODO(RELEASE-BLOCKER) comment) to prevent accidental release with the empty address.

Fix this →


High: Address comparison is case-sensitive — mixed-case checksummed addresses can fail

x/cronos/keeper/evmhandlers/send_cro_to_ibc.go:75

if authorizedBridge == "" || common.HexToAddress(authorizedBridge) != contract {

common.HexToAddress normalizes hex to a 20-byte common.Address, and common.Address equality compares raw bytes, so common.HexToAddress("0xAbCd...") == common.HexToAddress("0xabcd...") is true. The comparison is therefore case-insensitive at the byte level, which is correct. ✅

However, the param validation (validateIsEvmAddress) accepts any mixed-case EIP-55 checksummed address string via common.IsHexAddress. This is fine because the comparison goes through HexToAddress on both sides. No issue here — this is a false alarm resolved on re-read.


Medium: Test cases share mutable param state without explicit reset

x/cronos/keeper/evmhandlers_test.go:380–401

The second test case ("unauthorized contract (different address in param)") sets CroBridgeContractAddress = 0x999..., but suite.SetupTest() is called before malleate for each case (line 471 in the loop). Since SetupTest reinitializes the app, this is actually fine. However, the first test case ("unauthorized contract (param not set)") implicitly depends on DefaultParams having CroBridgeContractAddress = "" — this is correct but fragile if DefaultParams ever changes. A defensive explicit reset (params.CroBridgeContractAddress = "") in the first case's malleate would make the intent clearer.


Medium: Integration test query_params() contract address round-trip not validated

integration_tests/test_ibc.py:194–195

gov_params = cli.query_params()
gov_params["cro_bridge_contract_address"] = contract.address

contract.address from web3.py is EIP-55 checksummed (mixed-case). The governance MsgUpdateParams path calls Validate()validateIsEvmAddress which accepts it. But downstream, common.HexToAddress(authorizedBridge) normalizes it. This round-trip is correct, but the integration test doesn't assert that after the proposal passes, the stored cro_bridge_contract_address matches contract.address (case-insensitively). Adding that assertion would increase confidence.


Low: Missing changelog entry

The github-actions bot already flagged this. No entry has been added to CHANGELOG.md for this security fix. Per repo convention (seen in the existing ## UNRELEASED > ### Bug fixes section), this should be added under Bug fixes.


Low: validateIsEvmAddress error message is param-name-specific

x/cronos/types/params.go:155-157

if (common.HexToAddress(s) == common.Address{}) {
    return fmt.Errorf("cro_bridge_contract_address must not be the zero address")
}

The error hardcodes cro_bridge_contract_address but validateIsEvmAddress is a generic validator registered via ParamSetPairs. If this validator is ever reused for a different param, the error message will be misleading. Consider making the error message generic: "evm address must not be the zero address".

Fix this →


Design Notes (non-blocking)

  • Sister handler alignment: SendToIbc and SendToAccount gate via the token-mapping registry (GetDenomByContract), which is a many-to-many relationship. This PR uses a single-address allowlist param, which is appropriate here because SendCroToIbc has exactly one privileged contract (the CroBridge). The design divergence is intentional and correct.
  • Empty-string-disables semantics: Using "" to disable the hook is explicit and safe. The validateIsEvmAddress guard (which allows empty) and the handler check (which rejects empty) together form a clean API. ✅
  • Proto field number 6: New field added without removing or reusing existing field numbers. Migration is backward-compatible for proto decoding (old messages just won't have the field populated). ✅

Comment thread app/upgrades.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/upgrades.go (1)

22-25: Add a release-time guard for the mainnet bridge address.

mainnetCroBridgeContractAddress still defaults to "", and the current upgrade tests explicitly accept that value. If this is missed during a mainnet cut, v1.8 will silently leave SendCroToIbc disabled chain-wide after upgrade. Consider making CI or release packaging fail when this constant is empty for the mainnet build.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/upgrades.go` around lines 22 - 25, The constant
mainnetCroBridgeContractAddress is left as an empty string which silently
disables SendCroToIbc; add a release-time guard that fails fast when building or
running a mainnet release: implement a validation check (e.g. in an init() or an
upgrade validation function) that asserts mainnetCroBridgeContractAddress is
non-empty for mainnet builds and triggers a clear panic/exit or returns an error
during startup/packaging, or add a CI/release packaging step to reject builds
where mainnetCroBridgeContractAddress == ""; reference the symbol
mainnetCroBridgeContractAddress and the SendCroToIbc hook to locate where to
enforce the guard.
x/cronos/keeper/evmhandlers_test.go (1)

368-373: ⚡ Quick win

Use SendCroToIbcEvent ABI in TestSendCroToIbcHandler payload setup.

These cases currently pack data with SendToIbcEvent. It works only while both event input layouts stay identical, which weakens this test’s contract-specific coverage.

Proposed patch
-                input, _ := evmhandlers.SendToIbcEvent.Inputs.NonIndexed().Pack(
+                input, _ := evmhandlers.SendCroToIbcEvent.Inputs.NonIndexed().Pack(
                     sender,
                     "recipient",
                     coin.Amount.BigInt(),
                 )
-                input, _ := evmhandlers.SendToIbcEvent.Inputs.NonIndexed().Pack(
+                input, _ := evmhandlers.SendCroToIbcEvent.Inputs.NonIndexed().Pack(
                     sender,
                     "recipient",
                     coin.Amount.BigInt(),
                 )
-                input, _ := evmhandlers.SendToIbcEvent.Inputs.NonIndexed().Pack(
+                input, _ := evmhandlers.SendCroToIbcEvent.Inputs.NonIndexed().Pack(
                     sender,
                     "recipient",
                     coin.Amount.BigInt(),
                 )

Also applies to: 392-397, 446-450

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@x/cronos/keeper/evmhandlers_test.go` around lines 368 - 373, The test is
packing event payloads with evmhandlers.SendToIbcEvent but the test is
specifically for SendCroToIbcHandler; change the payload packing to use the
SendCroToIbcEvent ABI (e.g.
evmhandlers.SendCroToIbcEvent.Inputs.NonIndexed().Pack(...)) wherever
SendToIbcEvent is used in TestSendCroToIbcHandler (and the other occurrences
noted), so the packed input uses the contract-specific SendCroToIbcEvent layout
instead of SendToIbcEvent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@x/cronos/simulation/genesis.go`:
- Line 84: The GetOrGenerate block that populates maxCallbackGas is mistakenly
writing into ibcTimeout instead of the maxCallbackGas variable, so when
AppParams supplies max_callback_gas it gets ignored and 0 is persisted; update
the GetOrGenerate usage to read/write the correct key into maxCallbackGas (not
ibcTimeout) so that the later call to types.NewParams(ibcCroDenom, ibcTimeout,
cronosAdmin, enableAutoDeployment, maxCallbackGas, "") receives the intended
value. Locate the GetOrGenerate invocation and replace the ibcTimeout target
with maxCallbackGas and ensure the AppParams key name matches
"max_callback_gas".

---

Nitpick comments:
In `@app/upgrades.go`:
- Around line 22-25: The constant mainnetCroBridgeContractAddress is left as an
empty string which silently disables SendCroToIbc; add a release-time guard that
fails fast when building or running a mainnet release: implement a validation
check (e.g. in an init() or an upgrade validation function) that asserts
mainnetCroBridgeContractAddress is non-empty for mainnet builds and triggers a
clear panic/exit or returns an error during startup/packaging, or add a
CI/release packaging step to reject builds where mainnetCroBridgeContractAddress
== ""; reference the symbol mainnetCroBridgeContractAddress and the SendCroToIbc
hook to locate where to enforce the guard.

In `@x/cronos/keeper/evmhandlers_test.go`:
- Around line 368-373: The test is packing event payloads with
evmhandlers.SendToIbcEvent but the test is specifically for SendCroToIbcHandler;
change the payload packing to use the SendCroToIbcEvent ABI (e.g.
evmhandlers.SendCroToIbcEvent.Inputs.NonIndexed().Pack(...)) wherever
SendToIbcEvent is used in TestSendCroToIbcHandler (and the other occurrences
noted), so the packed input uses the contract-specific SendCroToIbcEvent layout
instead of SendToIbcEvent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1555cfe1-e7fd-4784-9b88-2a9f6611cba7

📥 Commits

Reviewing files that changed from the base of the PR and between 8eeea2e and 9bdda04.

⛔ Files ignored due to path filters (1)
  • x/cronos/types/cronos.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (9)
  • app/upgrades.go
  • app/upgrades_test.go
  • integration_tests/test_ibc.py
  • integration_tests/test_upgrade.py
  • proto/cronos/cronos.proto
  • x/cronos/keeper/evmhandlers/send_cro_to_ibc.go
  • x/cronos/keeper/evmhandlers_test.go
  • x/cronos/simulation/genesis.go
  • x/cronos/types/params.go

Comment thread x/cronos/simulation/genesis.go Outdated
@codecov

codecov Bot commented Jun 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 13.33333% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 14.01%. Comparing base (a3c2f70) to head (e3b2dba).
⚠️ Report is 208 commits behind head on main.

Files with missing lines Patch % Lines
x/cronos/types/params.go 16.66% 12 Missing and 3 partials ⚠️
app/upgrades.go 14.28% 5 Missing and 1 partial ⚠️
x/cronos/keeper/evmhandlers/send_cro_to_ibc.go 0.00% 5 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2099      +/-   ##
==========================================
- Coverage   16.87%   14.01%   -2.87%     
==========================================
  Files          72       75       +3     
  Lines        6163     5146    -1017     
==========================================
- Hits         1040      721     -319     
+ Misses       5000     4343     -657     
+ Partials      123       82      -41     
Files with missing lines Coverage Δ
x/cronos/keeper/evmhandlers/send_cro_to_ibc.go 0.00% <0.00%> (-58.83%) ⬇️
app/upgrades.go 48.57% <14.28%> (+43.23%) ⬆️
x/cronos/types/params.go 49.31% <16.66%> (+14.17%) ⬆️

... and 70 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

JayT106 added 3 commits June 4, 2026 21:50
- Make validateIsEvmAddress error message generic (not param-name-specific)
- Explicitly reset CroBridgeContractAddress in test malleate for clarity
- Add round-trip assertion in integration test for cro_bridge_contract_address
…able

ibcTimeout was being used as the target pointer instead of maxCallbackGas,
causing max_callback_gas from AppParams to be ignored and 0 persisted.
…est env

Port nix/testenv.nix fixes from crypto-org-chain#2091 to unblock integration_tests CI:
- Add preferWheels = true to mkPoetryEnv
- Clear postPatch for ckzg wheel builds (no src/Makefile in wheel)
- Clear preConfigure for eth-hash/rlp/eth-keyfile/eth-keys/web3 wheel builds
- Guard typing-extensions/types-requests postPatch to source-only builds
@github-actions github-actions Bot added the nix label Jun 5, 2026
JayT106 added 2 commits June 4, 2026 22:13
Lines 211-213 exceeded 88-char limit and failed Black formatting check.
Extract assertion variables to shorten lines.
- Fix import ordering in x/cronos/types/params.go (golangci-lint --fix)
- Add missing blank line in app/upgrades_test.go (golangci-lint --fix)
- Add cro_bridge_contract_address field to test_gov_update_params expected params
Comment thread x/cronos/types/params.go Outdated
JayT106 added 2 commits June 5, 2026 12:33
Supports multiple bridge implementations and testnets by changing
CroBridgeContractAddress (string) to CroBridgeContractAddresses ([]string)
in params, proto, and the SendCroToIbc authorization check.
Rename cro_bridge_contract_address -> cro_bridge_contract_addresses and
update all integration test references to use []string instead of string.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
x/cronos/types/params.go (2)

143-154: ⚡ Quick win

Empty strings in the address slice create validation inconsistency.

validateIsEvmAddresses allows empty strings within the slice because it delegates to validateIsEvmAddress, which returns nil for empty strings (line 161-163). However, the zero-address check (lines 167-169) rejects the explicit zero address hex string. This creates an inconsistency: "" is allowed but converts to the zero address via common.HexToAddress(""), while "0x0000000000000000000000000000000000000000" is explicitly rejected.

For a slice parameter, the "not set" state should be []string{} (empty slice), not a slice containing empty strings like []string{"", ""}. Allowing empty strings within the slice serves no purpose and may confuse operators who might mistakenly believe adding "" to the list has some effect.

🛡️ Proposed fix to reject empty strings in the slice

Reject empty strings in validateIsEvmAddresses before delegating to the single-address validator:

 func validateIsEvmAddresses(i interface{}) error {
 	addrs, ok := i.([]string)
 	if !ok {
 		return fmt.Errorf("invalid parameter type: %T", i)
 	}
 	for _, addr := range addrs {
+		if len(addr) == 0 {
+			return fmt.Errorf("address list must not contain empty strings")
+		}
 		if err := validateIsEvmAddress(addr); err != nil {
 			return err
 		}
 	}
 	return nil
 }

Alternatively, remove the empty-string early return from validateIsEvmAddress if it's only used for slice elements.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@x/cronos/types/params.go` around lines 143 - 154, validateIsEvmAddresses
currently lets empty strings pass through because validateIsEvmAddress treats ""
as valid; update validateIsEvmAddresses to explicitly reject empty elements
before calling validateIsEvmAddress by checking each addr == "" and returning a
descriptive error (e.g., "empty address in slice"), so slices must use
[]string{} for "not set"; alternatively, remove the empty-string early-return in
validateIsEvmAddress but prefer the slice-level check in validateIsEvmAddresses
to catch this case.

161-163: 💤 Low value

Document the rationale for allowing empty strings in single-address validation.

The early return for empty strings in validateIsEvmAddress is appropriate for optional single-address parameters like CronosAdmin (where "" means "not set"), but when reused for slice validation in validateIsEvmAddresses, it produces the confusing behavior flagged above.

Consider adding a comment here explaining that empty strings are permitted to support optional single-address fields, or split the validators into separate single-address and slice-element variants.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@x/cronos/types/params.go` around lines 161 - 163, The empty-string early
return in validateIsEvmAddress currently permits "" for optional single-address
params (e.g., CronosAdmin) but causes confusing behavior when reused by
validateIsEvmAddresses; either add a clear comment above validateIsEvmAddress
explaining that the empty string is intentionally allowed to represent "not set"
for single-value params, and note that callers validating slices should handle
empties separately, or refactor by creating two validators:
validateIsEvmAddressAllowEmpty (for single optional fields) and
validateIsEvmAddressNonEmpty (used by validateIsEvmAddresses for slice
elements), then update validateIsEvmAddresses to call the non-empty variant.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/upgrades_test.go`:
- Around line 69-73: The test TestUpgradeV18CroBridgeContractAddresses currently
only uses common.IsHexAddress to validate mainnetCroBridgeContractAddresses, but
production validation uses validateIsEvmAddresses → validateIsEvmAddress which
also rejects the zero address; update the test to mirror production validation
by either invoking validateIsEvmAddresses (or validateIsEvmAddress for each
entry) on each addr or add an explicit non-zero-address assertion (addr !=
common.Address{} / not "0x000...") so the test will fail with the same rule as
SetParams; reference the test function TestUpgradeV18CroBridgeContractAddresses
and the validators validateIsEvmAddresses / validateIsEvmAddress when making the
change.

In `@integration_tests/test_gov_update_params.py`:
- Line 55: The test is using the wrong parameter name and type: replace the
singular string field "cro_bridge_contract_address" with the plural slice field
matching the proto/params definition (CroBridgeContractAddresses) and provide a
list (e.g., [] or ["..."]) instead of an empty string; update the test data in
integration_tests/test_gov_update_params.py to set CroBridgeContractAddresses
(matching the x/cronos/types/params.go CroBridgeContractAddresses []string) so
the governance proposal uses the correct field and type.

---

Nitpick comments:
In `@x/cronos/types/params.go`:
- Around line 143-154: validateIsEvmAddresses currently lets empty strings pass
through because validateIsEvmAddress treats "" as valid; update
validateIsEvmAddresses to explicitly reject empty elements before calling
validateIsEvmAddress by checking each addr == "" and returning a descriptive
error (e.g., "empty address in slice"), so slices must use []string{} for "not
set"; alternatively, remove the empty-string early-return in
validateIsEvmAddress but prefer the slice-level check in validateIsEvmAddresses
to catch this case.
- Around line 161-163: The empty-string early return in validateIsEvmAddress
currently permits "" for optional single-address params (e.g., CronosAdmin) but
causes confusing behavior when reused by validateIsEvmAddresses; either add a
clear comment above validateIsEvmAddress explaining that the empty string is
intentionally allowed to represent "not set" for single-value params, and note
that callers validating slices should handle empties separately, or refactor by
creating two validators: validateIsEvmAddressAllowEmpty (for single optional
fields) and validateIsEvmAddressNonEmpty (used by validateIsEvmAddresses for
slice elements), then update validateIsEvmAddresses to call the non-empty
variant.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c41fdbb9-73b6-43c7-8a88-fc9721aced87

📥 Commits

Reviewing files that changed from the base of the PR and between 8783720 and bee8c39.

⛔ Files ignored due to path filters (1)
  • x/cronos/types/cronos.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (8)
  • app/upgrades.go
  • app/upgrades_test.go
  • integration_tests/test_gov_update_params.py
  • proto/cronos/cronos.proto
  • x/cronos/keeper/evmhandlers/send_cro_to_ibc.go
  • x/cronos/keeper/evmhandlers_test.go
  • x/cronos/simulation/genesis.go
  • x/cronos/types/params.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • proto/cronos/cronos.proto
  • x/cronos/simulation/genesis.go
  • x/cronos/keeper/evmhandlers_test.go
  • app/upgrades.go

Comment thread app/upgrades_test.go Outdated
Comment thread integration_tests/test_gov_update_params.py Outdated
JayT106 and others added 2 commits June 5, 2026 12:41
Panic early in RegisterUpgradeHandlers if any address is not a valid
non-zero EVM hex address, catching misconfiguration before upgrade height.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@integration_tests/test_ibc.py`:
- Around line 211-214: The comprehension and long assertion message in the test
are too long for lint; reformat the list comprehension and the assert into
multiple lines so Black/flake8 pass. For example, split the stored_addresses
assignment that uses stored_params.get("cro_bridge_contract_addresses", [])
across lines and break the assert into a short condition line and a separate
indented tuple/message expression referencing contract.address and stored_params
so the lines are under the max length; make changes around stored_addresses,
stored_params and contract.address.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ba252717-38cf-4ca5-a665-8740c7a273d7

📥 Commits

Reviewing files that changed from the base of the PR and between bee8c39 and 18abb7f.

📒 Files selected for processing (3)
  • integration_tests/test_gov_update_params.py
  • integration_tests/test_ibc.py
  • integration_tests/test_upgrade.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • integration_tests/test_upgrade.py

Comment thread integration_tests/test_ibc.py Outdated
JayT106 and others added 5 commits June 5, 2026 12:52
Proto marshal/unmarshal round-trips []string{} as nil; TestMigrate
compares DefaultParams() against the deserialized value and failed.
Remove the explicit empty slice so both sides are nil after round-trip.

Also reverts gofumpt struct-field alignment to match the longest field
(EnableAutoDeployment) now that CroBridgeContractAddresses is omitted
from the literal.
Comment thread app/upgrades.go Outdated
Comment thread app/upgrades.go Outdated
JayT106 and others added 2 commits June 15, 2026 09:55
@thomas-nguy
thomas-nguy enabled auto-merge June 17, 2026 02:12
@thomas-nguy
thomas-nguy added this pull request to the merge queue Jun 17, 2026
Merged via the queue into crypto-org-chain:main with commit 2669a65 Jun 17, 2026
51 of 54 checks passed
JayT106 added a commit to JayT106/cronos that referenced this pull request Jun 18, 2026
…rypto-org-chain#2099)

* fix(cronos): add contract authorization check to SendCroToIbcHandler

Any EVM contract emitting __CronosSendCroToIbc could drain CRO via IBC
without being registered or authorized. Sister handlers (SendToIbc,
SendToAccount) already gate on contract registration; this handler had
no equivalent guard.

Fix:
- Add CroBridgeContractAddress string param to cronos module params
- SendCroToIbcHandler rejects callers that don't match the authorized
  address; empty param (safe default) disables the hook entirely
- validateIsEvmAddress rejects non-hex strings and the zero address
- v1.8 upgrade handler migrates the param (mainnetCroBridgeContractAddress
  constant must be filled in before release)

Tests:
- Unit: two new rejection cases + existing success cases updated to set
  the authorized address before calling the handler
- Go upgrade test: validates constant format + SetParams round-trip
- Integration upgrade test: asserts param is "" after v1.8 handler runs
- Integration IBC test: authorizes deployed CroBridge via gov proposal
  before bridge call (fixes test breakage from the new guard)

* update changelog

* fix(cronos): address review findings from PR crypto-org-chain#2099

- Make validateIsEvmAddress error message generic (not param-name-specific)
- Explicitly reset CroBridgeContractAddress in test malleate for clarity
- Add round-trip assertion in integration test for cro_bridge_contract_address

* fix(cronos): fix maxCallbackGas GetOrGenerate writing into wrong variable

ibcTimeout was being used as the target pointer instead of maxCallbackGas,
causing max_callback_gas from AppParams to be ignored and 0 persisted.

* fix(nix): add preferWheels and wheel-safe overrides for integration test env

Port nix/testenv.nix fixes from crypto-org-chain#2091 to unblock integration_tests CI:
- Add preferWheels = true to mkPoetryEnv
- Clear postPatch for ckzg wheel builds (no src/Makefile in wheel)
- Clear preConfigure for eth-hash/rlp/eth-keyfile/eth-keys/web3 wheel builds
- Guard typing-extensions/types-requests postPatch to source-only builds

* fix(test): fix python lint errors in test_ibc.py

Lines 211-213 exceeded 88-char limit and failed Black formatting check.
Extract assertion variables to shorten lines.

* fix(ci): fix golangci-lint and gov param test failures

- Fix import ordering in x/cronos/types/params.go (golangci-lint --fix)
- Add missing blank line in app/upgrades_test.go (golangci-lint --fix)
- Add cro_bridge_contract_address field to test_gov_update_params expected params

* fix(cronos): expand CroBridgeContractAddress to a list

Supports multiple bridge implementations and testnets by changing
CroBridgeContractAddress (string) to CroBridgeContractAddresses ([]string)
in params, proto, and the SendCroToIbc authorization check.

* fix(integration-tests): update CroBridge param to address list

Rename cro_bridge_contract_address -> cro_bridge_contract_addresses and
update all integration test references to use []string instead of string.

* fix(cronos): validate mainnetCroBridgeContractAddresses at startup

Panic early in RegisterUpgradeHandlers if any address is not a valid
non-zero EVM hex address, catching misconfiguration before upgrade height.

* fix(ci): fix gci import order in app/upgrades.go

* fix(test): fix python lint line-length in test_ibc.py

* fix(cronos): use nil default for CroBridgeContractAddresses

Proto marshal/unmarshal round-trips []string{} as nil; TestMigrate
compares DefaultParams() against the deserialized value and failed.
Remove the explicit empty slice so both sides are nil after round-trip.

Also reverts gofumpt struct-field alignment to match the longest field
(EnableAutoDeployment) now that CroBridgeContractAddresses is omitted
from the literal.

* feat(upgrade): set mainnet CroBridge contract addresses for v1.8

Add the two deployed CroBridge contract addresses and rename variable
from mainnetCroBridgeContractAddresses to croBridgeContractAddresses.

* fix(upgrade): update test to use renamed croBridgeContractAddresses

* fix(test): update upgrade test to expect populated croBridge addresses

* fix: inline evm address validation and remove unused validateIsEvmAddress

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Thomas Nguy <thomas.nguy@crypto.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants